
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <string>
#include <string.h>
#include <stdio.h>

using namespace std;

#define SOCKET_ERROR        -1
static char buf[256];

#include "CConnection.h"

void dbgMsg_displayConnectionContext(const char* func, const char* msg, const CConnection& con);

CConnection::CConnection()
{
}

CConnection::~CConnection()
{
}

void CConnection::init(int port, char* pName)
{
    m_port = port;
    m_sock = -1;
    m_client = -1;
    m_state = CONNECTION_STATE_INIT;
    m_thread_handle = -1;
    strcpy(m_name, pName);
}

string CConnection::displayText() const
{
    sprintf(buf, "%s Connection port: %d  sock: %d  client: %d  state: %s", m_name, m_port, m_sock, m_client, connectionStateToText(m_state).c_str());
    return buf;
}

string CConnection::connectionStateToText(CONNECTION_STATE eState)
{
    string sRet;
    switch (eState)
    {
    case CONNECTION_STATE_RESET:
        sRet = "CONNECTION_STATE_RESET";
        break;

    case CONNECTION_STATE_INIT:
        sRet = "CONNECTION_STATE_INIT";
        break;

    case CONNECTION_STATE_BIND_OK:
        sRet = "CONNECTION_STATE_BIND_OK";
        break;

    case CONNECTION_STATE_ACCEPT_OK:
        sRet = "CONNECTION_STATE_ACCEPT_OK";
        break;

    default:
        sRet = "****";
        break;
    }
    return sRet;
}

int CConnection::serverBind()
{
    int tempSock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    struct sockaddr_in address;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_family = AF_INET;
    address.sin_port = ::htons(m_port);

    if (::bind(tempSock, (struct sockaddr*) & address, sizeof (struct sockaddr_in)) == SOCKET_ERROR)
    {
        sprintf(buf, "error %d : couldn't bind ", errno);
        dbgMsg_displayConnectionContext(__FUNCTION__, buf, *this);
        close(tempSock);
        tempSock = -1;
    }
    else
    {
        if (::listen(tempSock, 10) == SOCKET_ERROR)
        {
            sprintf(buf, "error %d : couldn't listen ", errno);
            dbgMsg_displayConnectionContext(__FUNCTION__, buf, *this);
            close(tempSock);
            tempSock = -1;
        }
        return tempSock;
    }
}

int CConnection::acceptConnection()
{
    //
    // Accept will block
    //
    unsigned int addrlen = sizeof (struct sockaddr);
    struct sockaddr_in address = {0};
    int client = ::accept(m_sock, (struct sockaddr*) & address, &addrlen);
    if (client == SOCKET_ERROR)
    {
        sprintf(buf, "error %d : couldn't accept ", errno);
        dbgMsg_displayConnectionContext(__FUNCTION__, buf, *this);
        client = -1;
    }
    return client;
}

void CConnection::resetConnection()
{
    dbgMsg_displayConnectionContext(__FUNCTION__, "Before reset connection - ", *this);
    if ((m_state == CONNECTION_STATE_BIND_OK)
            || (m_state == CONNECTION_STATE_ACCEPT_OK))
    {
        m_state = CONNECTION_STATE_RESET;
        dbgMsg_displayConnectionContext(__FUNCTION__, "Resetting connection", *this);

        fflush(NULL);
        close(m_client);
        m_client = -1;
        close(m_sock);
        m_sock = -1;

        m_state = CONNECTION_STATE_INIT;
    }
}

void CConnection::tryToBind()
{
    if (m_sock != -1)
    {
        close(m_sock);
        m_sock = -1;
    }
    m_sock = serverBind();
    if (m_sock == -1)
    {
        m_state = CONNECTION_STATE_INIT;
    }
    else
    {
        m_state = CONNECTION_STATE_BIND_OK;
        dbgMsg_displayConnectionContext(__FUNCTION__, "Bind OK", *this);
    }
}

void CConnection::tryToAccept()
{
    if (m_state == CONNECTION_STATE_BIND_OK)
    {
        dbgMsg_displayConnectionContext(__FUNCTION__, "Trying to accept", *this);
        int tempClient = acceptConnection();
        if (tempClient == -1)
        {
            dbgMsg_displayConnectionContext(__FUNCTION__, "Accept failed", *this);
        }
        else
        {
            int browserBytes = 0;
            char browser_request[1024];
            browserBytes = read(tempClient, browser_request, 1023);
            m_client = tempClient;
            calledOnceAfterAccept();
            m_state = CONNECTION_STATE_ACCEPT_OK;
            dbgMsg_displayConnectionContext(__FUNCTION__, "Accept OK", *this);
        }
    }
}

int CConnection::writeClient(char* pBuf, unsigned int siz)
{
    return write(m_client, pBuf, siz);
}
